﻿using SocketBook7Server.Script.logic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SocketBook7Server.Script.net
{
    class NetManager
    {
        //监听Socket
        public static Socket listenfd;
        //客户端Socket及状态信息
        public static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
        static List<Socket> checkRead = new List<Socket>();
        //ping间隔
        public static long pingInterval = 10;
        public static void StartLoop(int listenPort)
        {
            listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //Bind
            IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEp = new IPEndPoint(ipAdr, listenPort);
            listenfd.Bind(ipEp);
            //Listen
            listenfd.Listen(1000);
            Console.WriteLine("[服务器]启动成功");
            while (true)
            {
                ResetCheckRead();
                //确定一个或多个状态的静态方法socket实例
                lock (checkRead)
                {
                    try
                    {
                        Socket.Select(checkRead, null, null, 1000);
                    }
                    catch (SocketException ex)
                    {
                        Console.WriteLine("Select fail: " + ex.ToString());
                    }
                }

                for (int i = checkRead.Count - 1; i >= 0; --i)
                {
                    Socket s = checkRead[i];
                    //如果是第一次连接客户端 
                    if (s == listenfd)
                    {
                        ReadListenfd(s);
                    }
                    else
                    {
                        //ReadClientfd(s);  在其他地方调用了 这里以后不再调用了
                    }
                }
                //超时
                Timer();
            }
        }
        //发送
        public static void Send(ClientState cs, MsgBase msg)
        {
            //状态判断
            if (cs == null)
            {
                return;
            }
            if (!cs.socket.Connected)
            {
                return;
            }
            //数据编码
            byte[] bodyBytes = MsgBase.Encode(msg);
            int len = bodyBytes.Length;
            byte[] sendBytes = new byte[2 + len];
            //组装长度
            sendBytes[0] = (byte)(len % 256);
            sendBytes[1] = (byte)(len / 256);
            //组装消息体
            Array.Copy(bodyBytes, 0, sendBytes, 2, bodyBytes.Length);
            //MsgBase msgBase = MsgBase.Decode(sendBytes, 2, (UInt16)(sendBytes.Length-2));
            //string s = MsgBase.GetString(msgBase);
            //Console.WriteLine("\nBeginSend: " + s);
            //为简化代码，不设置回调
            try
            {
                cs.socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, null, null);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Socket Close on BeginSend" + ex.ToString());
            }
        }
        //定时器
        static void Timer()
        {
            //消息分发
            MethodInfo mei = typeof(EventHandler).GetMethod("OnTimer");
            object[] ob = { };
            mei.Invoke(null, ob);
        }
        //关闭连接
        public static void Close(ClientState state)
        {
            Socket socket = state.socket;
            //状态判断
            if (socket == null || !socket.Connected)
            {
                return;
            }
            //消息分发
            MethodInfo mei = typeof(EventHandler).GetMethod("OnDisconnect");
            object[] ob = { state };
            //调用由此 MethodInfo 实例反射的方法或构造函数。
            mei.Invoke(null, ob);
            //关闭
            try
            {
                Console.WriteLine("Close: " + state.socket.RemoteEndPoint.ToString());
                state.socket.Shutdown(SocketShutdown.Send);
                state.socket.Disconnect(false);
                //下面的方法 会有bug  断开连接后服务端报错 错误的客户端请求。。。
                //LingerOption lingerOption = new LingerOption(true, 10);
                //state.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
                //state.socket.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine("Disconnect fail " + e.ToString());
                return;
            }
            
            lock (clients)
            {
                clients.Remove(state.socket);
            }
            ResetCheckRead();
        }
        //读取Clientfd
        public static void ReadClientfd(Socket clientfd)
        {
            ClientState state;
            lock (clients)
            { state = clients[clientfd]; }
            ByteArray readBuff = state.readBuff;
            //接收
            //int count = 0;
            //缓冲区不够，清除，若依旧不够，只能返回
            //缓冲区长度只有1024，单条协议超过缓冲区长度时会发生错误，根据需要调整长度
            if (readBuff.remain <= 0)
            {
                OnReceiveData(state);
                readBuff.MoveBytes();
            };
            if (readBuff.remain <= 0)
            {
                Console.WriteLine("Receive fail , maybe msg length > buff capacity");
                Close(state);
                return;
            }
            clientfd.BeginReceive(readBuff.bytes, 0, readBuff.remain, 0,
        new AsyncCallback(ReceiveCallback), state);
            //移动缓冲区
            readBuff.CheckAndMoveBytes();
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                ClientState state = (ClientState)ar.AsyncState;
                Socket socket = state.socket;
                //获取接收数据长度
                int count = 0;
                //if (socket.Connected)
                    count = socket.EndReceive(ar);
                if (count == 0)
                {
                    Console.WriteLine("count = socket.EndReceive(ar);");
                    Close(state);
                    return;
                }
                ByteArray readBuff = state.readBuff;
                //消息处理
                readBuff.writeIdx += count;
                //处理二进制消息
                OnReceiveData(state);
                //继续接收数据
                if (readBuff.remain < 8)
                {
                    readBuff.ReSize(readBuff.length);
                }
                if (socket.Connected)
                    socket.BeginReceive(readBuff.bytes, readBuff.writeIdx,
                        readBuff.remain, 0, ReceiveCallback, state);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Receive fail: " + ex.ToString());
            }
        }

        //数据处理
        public static void OnReceiveData(ClientState state)
        {
            ByteArray readBuff = state.readBuff;
            //消息长度
            if (readBuff.length <= 2)
            {
                return;
            }
            UInt16 bodyLength = readBuff.ReadUInt16();//消息体
            if (readBuff.length < bodyLength + 2)
            {
                readBuff.ReSize(bodyLength);
                return;
            }
            //如果接收了一条完整的消息
            readBuff.readIdx += 2;
            //解析协议名
            int nameCount = 0;
            MsgBase msgBase = MsgBase.Decode(readBuff.bytes, readBuff.readIdx,(UInt16)bodyLength);
            #region 打印接收到的消息
            string s = MsgBase.GetString(msgBase);
            Console.WriteLine("OnReceiveData: " + s);
            #endregion
            string protoName = msgBase.protoName;
            nameCount = protoName.Length;
            if (protoName == "")
            {
                Console.WriteLine("OnReceiveData MsgBase.DecodeName fail");
                Close(state);
                return;
            }
            readBuff.readIdx += nameCount;
            //解析协议体
            int bodyCount = bodyLength - nameCount;

            //MsgBase msgBase = MsgBase.Decode(protoName, readBuff.bytes, readBuff.readIdx, bodyCount);
            readBuff.readIdx += bodyCount;
            readBuff.CheckAndMoveBytes();
            //分发消息
            MethodInfo mi = typeof(MsgHandler).GetMethod(protoName);
            object[] o = { state, msgBase };
            Console.WriteLine("Receive " + protoName);
            if (mi != null)
            {
                mi.Invoke(null, o);
            }
            else
            {
                Console.WriteLine("OnReceiveData Invoke fail " + protoName);
            }
            //继续读取消息
            if (readBuff.length > 2)
            {
                OnReceiveData(state);
            }
        }
        //读取Listenfd 开始接受客户端传入的连接请求
        public static void ReadListenfd(Socket s)
        {
            try
            {
                listenfd.BeginAccept(new AsyncCallback(AcceptCallback), listenfd);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Accept fail" + ex.ToString());
            }
        }

        private static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                Socket clientfd = (Socket)ar.AsyncState;
                Socket handler = clientfd.EndAccept(ar);
                Console.WriteLine("Accept " + handler.RemoteEndPoint.ToString());
                ClientState state = new ClientState();
                state.socket = handler;
                lock (clients)
                { clients.Add(handler, state); }
               

                ReadClientfd(handler);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Accept  fail " + ex.ToString());
            }

        }

        //填充checkRead列表
        public static void ResetCheckRead()
        {
            lock (checkRead)
            {
                checkRead.Clear();
                checkRead.Add(listenfd);
                lock (clients)
                {
                    //foreach (ClientState s in clients.Values)
                    //{
                    //    checkRead.Add(s.socket);
                    //}
                    for (int i = 0; i < clients.Count ; i++)
                    {
                        var s = clients.ElementAt(i);
                        //if(s.Value.socket==null)
                            checkRead.Add(s.Value.socket);
                    }
                }
            }
        }
        //获取时间戳
        public static long GetTimeStamp()
        {
            //现在的时间减去从1970年一月一日零点开始的时间
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            //转换成秒数
            return Convert.ToInt64(ts.TotalSeconds);
        }
    }
}
